/* $Id: class-tptable.prg,v 1.0 2008/11/12 21:26:13 riztan Exp $*/
/*
	Copyright © 2008  Riztan Gutierrez <riztang@gmail.org>

   Este programa es software libre: usted puede redistribuirlo y/o modificarlo 
   conforme a los términos de la Licencia Pública General de GNU publicada por
   la Fundación para el Software Libre, ya sea la versión 3 de esta Licencia o 
   (a su elección) cualquier versión posterior.

   Este programa se distribuye con el deseo de que le resulte útil, pero 
   SIN GARANTÍAS DE NINGÚN TIPO; ni siquiera con las garantías implícitas de
   COMERCIABILIDAD o APTITUD PARA UN PROPÓSITO DETERMINADO. Para más información, 
   consulte la Licencia Pública General de GNU.

   http://www.gnu.org/licenses/
*/

/**\file class-ttabla.prg
 * \class TPTable. Clase TPTable
 *
 *  Clase para el manejo y control de tablas en tepuy
 *
 *  
 *  \see Add(). (esto es una prueba en la documentacion)
 */


/*
 *  La Clase TPTable, buscará los datos de la tabla solicitada en 
 *  el objeto principal, oTepuy. De no conseguirlos, procederá a buscarlos 
 *  en el maestro de tablas de Tepuy (tp_tables). 
*/
// GLOBAL EXTERNAL oTepuy  /** \var GLOBAL oTepuy. Objeto Principal oTepuy. */
memvar oTepuy

#include "gclass.ch"
#include "hbclass.ch"
#include "proandsys.ch"

CLASS TPTable

   Public:

   DATA nNumTab      AS NUMERIC INIT 0    // Para Indicar el numero de tabla
   DATA cTabla       AS CHARACTER         // Nombre de la tabla
   DATA cDescri      AS CHARACTER         // Descripción de la tabla
   DATA nId          AS NUMERIC           // Indica el Nro de la Conexión de la Tabla
   
   DATA lMobil       AS LOGICAL INIT .T.  // Indica si la puede estar fuera de la principal (tp_tablas).
   DATA lRemota      AS LOGICAL INIT .F.  // Indica si la tabla tiene una ubicación remota.
   DATA lEspejo      AS LOGICAL INIT .F.  // Indica si la tabla es espejo de una remota.

   DATA nRId         AS NUMERIC           // Indica la identificación de la conexión remota.
   DATA cRTabla      AS CHARACTER         // Indica el nombre de la tabla remota (si es espejo)

   DATA hVars        // Variables que controla esta clase
   DATA hConn        // Hash con Información de la Conexión de la tabla
   DATA hRConn       // Hash con Información de la Conexión Remota (Solo Espejo)
   DATA hRules       // Hash con las Reglas asociadas a la Tabla
   DATA hRoles       // Hash con los Roles asociados a la Tabla

//   DATA cName        // Para la consulta de las variables

   METHOD NEW( cTabla, lConsult )
   METHOD End()                 INLINE ::hVars := Hash()

   METHOD Import( )
   METHOD Export( )

ENDCLASS

//  ----  DIVISION

/*
   DATA  lAutoAdd    AS LOGICAL	 INIT .T.		
   DATA  lSensitive  AS LOGICAL	 INIT .F.		

   DATA  hVars       

   DATA  nPos        AS NUMERIC    INIT 0   // READONLY // [ER]
   DATA  cName       AS CHARACTER  INIT ""  // READONLY // [ER]

   METHOD New( lAutoAdd, lSensitive )          
   METHOD End()            INLINE ::Release()  

   METHOD Add( cName, xValue )                 
   METHOD Del( cName )             
   METHOD Get( cName ) 
   METHOD Set( cName, xValue )

   METHOD GetPos( cName )
   METHOD GetVar( nPos )

   METHOD IsDef( cName )   INLINE HHasKey( ::hVars, cName )

   METHOD Clone()          INLINE HClone( ::hVars )
   METHOD nCount()         INLINE Len( ::hVars )

   METHOD GetArray()           

   METHOD Release()        INLINE ::hVars := Hash()
*/

//   ERROR HANDLER OnError( cMsg, nError )

//ENDCLASS


/** Metodo Constructor.
 *  Permite generar la instancia de un objeto TPTable,
 *  Se puede inicializar con los parametros lAutomatic y lSensitive, 
 *  para definir si permite la creacion de variables y si admite 
 *  sensibilidad a las mayusculas respectivamente.
*/
METHOD New( cTabla, lCrea ) CLASS TPTable

   Local cPreSchema := oTepuy:oConn:Schema

   DEFAULT cTabla := "", lCrea := .F.

   ::hVars  := Hash()

   ::cTabla := cTabla

   HSetCaseMatch( ::hVars, .T. )

   If Empty(cTabla)
      Return .F.
   EndIf

   // --- Verificamos la conexion.
   If PQStatus(oTepuy:conn) != CONNECTION_OK
      MsgStop( MSG_LOST_CONNECTION , MSG_ALERT )
      QUIT   //  Forzamos la Salida, aunque pudieramos hacer un bucle de reconexion.
   EndIf


   // --- Ya tenemos nombre de tabla y conexion, ahora la buscamos
   //     en las recientemente traidas a memoria.
   If !HHasKey( ::hVars, cTabla )

      // --- No esta en el Hash. Debemos buscarla en tp_tablas.
      oTepuy:oConn:SetSchema( TP_SCHEMA_MAIN )
      If !( oTepuy:oConn:TableExists( TP_TABLE_MAIN ) )
//          MsgStop("No está la tabla '"+TP_TABLE_MAIN+CRLF+"'"+;
//                  "No se puede continuar.","ERROR")

         If oTepuy:oConn:TableExists( TP_TABLE_ENTITY )
            // --- Creamos un objeto de error y lo botamos
            Throw( ErrorNew( MSG_TABLE_NO_EXIST+TP_TABLE_MAIN, 0, 0, MSG_ACCESS_DENIED ) )
            Quit
         EndIf

         If !(MsgYesNo( MSG_TABLE_NO_EXIST+TP_TABLE_MAIN+"."+CRLF+;
            MSG_IMPORT_TABLES,"class-tp_table"))
            Salir()
            Throw( ErrorNew( "Error... No se puede continuar", 0, 0, MSG_ACCESS_DENIED ) )
            
         EndIf

         // --- Proceso de Importacion ya que aparentemente el sistema
         //     no está en la B.D.  Importacion de TP_TABLE_MAIN
         Import_Tables ( TP_TABLE_MAIN, oTepuy:oConn )

      EndIf
      oTepuy:oConn:SetSchema( cPreSchema )


      If ( oTepuy:oConn:TableExists( cTabla ) )

         MsgInfo("La tabla "+cTabla+" esta en la BD")

      Else
         If !lCrea

            MsgStop("La tabla "+cTabla+" NO esta en la BD."+CRLF+;
                    "<b>Pa Fuera!</b>")

         Else
   
            // --- Aqui debo llamar a una funcion que permita
            //     crear la tabla.
            MsgInfo("Debo crear la tabla "+cTabla)

         EndIf
      EndIf



/*
      HSetAutoAdd( ::hVars, .T. )
  
      ::lMobil  := .T.
      ::lRemota := .F.
      ::lEspejo := .F.
*/
      If( oTepuy:oConn:TableExists( cTabla ) )

                 

      EndIf


      // --- por continuar.  Riztan

   Else


   EndIf

   HSetAutoAdd( ::hVars, .F. )
   
RETURN Self

/** Metodo Import() 
 *  Lee un archivo xml del mismo nombre de la tabla y lo crea/actualiza en la base de datos
 */
METHOD Import( ) CLASS TPTable

   Local cFile := oTepuy:cTablas + ::cTabla + ".xml"

   LoadXML( cFile )
   
RETURN .F.


/** Metodo Export() 
 *  Graba la informacion de una tabla en un archivo xml con el mismo nombre de la tabla
 */
METHOD Export( ) CLASS TPTable

   Local cFile := oTepuy:cTablas + ::cTabla + ".xml"
   Local nFileHandle
   Local oXmlDoc, oXmlConnection, oXmlDatabase,oXmlLogin

   If File( cFile ) 

      If !MsgNoYes( MSG_FILE_EXIST+::cTabla+CRLF+;
                   MSG_REPLACE )
         Return Self
      EndIf

   EndIf

   oXmlDoc := TXmlDocument():new( '<?xml version="1.0"?>' )

   oXmlConnection := TXmlNode():new( , "conexion", { "nombre" => "postgresql" } )



   LoadXML( cFile )

RETURN .T.

/*

   LOCAL nFileHandle
   LOCAL oXmlDoc, oXmlConnection, oXmlDatabase,oXmlLogin

   oXmlDoc := TXmlDocument():new( '<?xml version="1.0"?>' )

   oXmlConnection := TXmlNode():new( , "conexion", { "nombre" => "postgresql" } )

   oXmlDoc:oRoot:addBelow( oXmlConnection )

   oXmlLogin := TXmlNode():new( , "lastlogin", { ;
                                  "nombre"     => oTepuy:cSystem_Name ,;
                                  "host"       => oTepuy:cHost        ,;
                                  "port"       => Alltrim(CStr(oTepuy:nPort)) ,;
                                  "database"   => oTepuy:cDB          ,;
                                  "user"       => oTepuy:cUser        ,;
                                  "password"   => oTepuy:cPass         ;
                                } )

   oXmlConnection:addBelow( oXmlLogin )

   nFileHandle := FCreate( oTepuy:cTemps+"login.xml" )
   // write the XML tree
   oXmlDoc:write( nFileHandle, HBXML_STYLE_INDENT )
   // close files
   FClose( nFileHandle )

*/


/*
 * \brief Importa las tablas principales a la Base de Datos.
 */
FUNCTION Import_Tables(cTabla , oConn)

   Local oXmlTables
   Local oXmlField

Msg_Info( "Import_Tables  = "+CStr(oTepuy:oConn:Schema) )

   If !LoadXML( oTepuy:cTablas+TP_TABLE_MAIN+".xml" )
      Throw( ErrorNew( MSG_ERROR_NO_CONTINUE, 0, 0, MSG_FILE_NO_EXIST ) )
      Quit
   EndIf

   oXmlTables := oTepuy:oTmpXML:FindFirst( TP_TABLE_MAIN )



//   If oXmlNode == NIL
//      Throw( ErrorNew( MSG_ERROR_NO_CONTINUE, 0, 0, MSG_FILE_NO_EXIST ) )
//   EndIf

//   oXmlTables:aAttributes[ "TP_TABLE_MAIN" ]

RETURN .F.



/*
 * \brief Exporta TP_TABLE_MAIN.
 */
// FUNCTION Export_Tables(cTabla , oConn)
FUNCTION Export_Tables( cTabla , oConn )

   Local aStruct, aField, aTemp, nFileHandle, hAttr
   Local cData, cPreSchema
   Local oXmlDoc, oXmlTable, oXmlStruct, oXmlNode
   Local oXmlField, oXmlRecord
   Local oXmlComment,oXmlSeq,oXmlRecSeq
   Local oXmlIndex,oXmlRecInd

   Local oQuery, aQuery, I, aFile,n:=1
   Local nOID, hComment

   DEFAULT cTabla := TP_TABLE_MAIN, oConn := oTepuy:oConn

   If AScan( oTepuy:aTabs_Main, {|a| cTabla == a } ) != 0
      oTepuy:oConn:SetSchema( TP_SCHEMA_MAIN )
   EndIf
  
   cPreSchema := oConn:Schema

   oQuery := oConn:Query("select * from "+cTabla )
   aStruct := oConn:TableStruct( cTabla, "postgres" )

   oXmlDoc := TXmlDocument():new( '<?xml version="1.0"?>' )

   oXmlTable := TXmlNode():new( , "tables", { "name" => cTabla } )
   oXmlDoc:oRoot:addBelow( oXmlTable )

   nOID := oConn:GetTable_OID(cTabla)

   MsgInfo( oConn:GetTable_Comment( cTabla, 1 ) )

   /*
    *  Generando la Estructura.
    */
// View( aStruct )

//MsgInfo( "OID = "+oTepuy:oConn:OID )

   oXmlStruct := TXmlNode():New( , "structure" )
   oXmlTable:addBelow( oXmlStruct )
   FOR EACH aField IN aStruct
      // store field information in XML attributes
      hAttr := { "name"      => Lower( aField[1] ), ;
                 "type"      => aField[2], ;
                 "len"       => LTrim( Str(aField[3]) ), ;
                 "dec"       => LTrim( Str(aField[4]) ), ;
                 "nullable"  => AllTrim( Upper(aField[5]) )}

      oXmlField := TXmlNode():new(, "field", hAttr )
      oXmlStruct:addBelow( oXmlField )
   NEXT

   //--- Comentarios
   oXmlComment := TXmlNode():New( , "comments" )
   oXmlTable:addBelow( oXmlComment )

   hAttr := { cTabla => DataToSql( AllTrim( oConn:GetTable_Comment( cTabla ) ) ) }   
   oXmlField := TXmlNode():New(, "table", hAttr )
   oXmlComment:addBelow( oXmlField )

   FOR EACH aField IN aStruct
      // store field information in XML attributes
      hAttr := { Lower( aField[1] ) => ;
                 DataToSql( AllTrim( oConn:GetTable_Comment( cTabla, n ) ) ) }
      n++
      oXmlField := TXmlNode():New(, "field", hAttr )
      oXmlComment:addBelow( oXmlField )
   NEXT


   //--- Seriales (Secuencias)
   n:=1
   oXmlSeq := TXmlNode():New( , "sequences" )
   oXmlTable:addBelow( oXmlSeq )

   FOR EACH aField IN aStruct
      aTemp:=oConn:pg_get_serial_sequence( cTabla, Lower( aField[1] ))
      FOR I := 1 to Len(aTemp[1])
         IF !Empty(aTemp[I,1])
            hAttr := { Lower( aField[1] ) => ;
                       DataToSql( aTemp[I,1] )}
            oXmlRecSeq := TXmlNode():New(, "seq_field", hAttr )
            oXmlSeq:addBelow( oXmlRecSeq )
         ENDIF
      NEXT I
   NEXT   


   //--- Indices 
   n:=1
   oXmlIndex := TXmlNode():New( , "indexes" )
   oXmlTable:addBelow( oXmlIndex )

   aTemp:=oConn:get_index_of_table( cTabla )
   If !Empty( aTemp )
      FOR EACH aField IN aTemp
         For i:=1 to Len(aTemp)
            hAttr := { "name" => ;
            	        DataToSql(aTemp[i]), ;
                       "def"  => ;
                       oConn:pg_get_indexdef( aTemp[i] ) }
            oXmlRecInd := TXmlNode():New(, "indexno_"+Alltrim(CStr(i)), hAttr )
            oXmlIndex:addBelow( oXmlRecInd )
         Next i
      NEXT   
   EndIf


//      MsgInfo( ValToPrg(aTemp) )
/*
         IF !Empty(aTemp)
            hAttr := { Lower( aField[1] ) => ;
                       DataToSql( aTemp[1] )}
            oXmlRecInd := TXmlNode():New(, "field", hAttr )
            oXmlSeq:addBelow( oXmlRecInd )
         ENDIF
*/


   //--- Registros
   oXmlNode := TXmlNode():New( , "records" )
   oXmlTable:addBelow( oXmlNode )
   DO WHILE .NOT. oQuery:Eof()
      hAttr      := { "recno" => LTrim( Str( oQuery:Recno() ) ) }
      oXmlRecord := TXmlNode():New( , "record", hAttr )

      n := 1

      FOR EACH aField IN aStruct

         IF aField[2] == "M"
            // Memo fields are written as CDATA
            cData     := oQuery:FieldGet( n , oQuery:Recno() )
            oXmlField := TXmlNode():New( HBXML_TYPE_CDATA , ;
                                         Lower( aField[1] ), ;
                                         NIL               , ;
                                         cData               )
         ELSE
            // other fields are written as normal tags
            cData     := oQuery:FieldGet( n , oQuery:Recno() )
            cData     := Alltrim( CStr( cData ) )
            oXmlField := TXmlNode():New( HBXML_TYPE_TAG    , ;
                                         Lower( aField[1] ), ;
                                         NIL               , ;
                                         cData               )
         ENDIF
         // add field node to record
         n++
         oXmlRecord:addBelow( oXmlField )
      NEXT
      // add record node to records
      oXmlNode:addBelow( oXmlRecord )

      oQuery:Skip(1)
   ENDDO

   oConn:SetSchema := cPreSchema
   
   nFileHandle := FCreate( oTepuy:cTablas + cTabla + ".xml" )
   oXmlDoc:write( nFileHandle, HBXML_STYLE_INDENT )
   // close files
   FClose( nFileHandle )

RETURN .F.
//EOF
